package com.itdfq.springboot_redis.controller;

import io.netty.util.internal.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @Author GocChin
 * @Date 2021/5/6 13:07
 * @Blog: itdfq.com
 * @QQ: 909256107
 * @Descript:
 */
@RestController
@RequestMapping("/redis")
public class Redis {

    /**
     * redisTemplate.opsForValue();//操作字符串
     * redisTemplate.opsForHash();//操作hash
     * redisTemplate.opsForList();//操作list
     * redisTemplate.opsForSet();//操作set
     * redisTemplate.opsForZSet();//操作有序set
     */
    @Autowired
    private RedisTemplate redisTemplate;



    //测试分布式锁
    @GetMapping("/testLock")
    public void testLock(){
        //获取锁，setne
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111");
        //获取锁成功
        if (lock){
            Object value = redisTemplate.opsForValue().get("num");
            //判断num为空
            if (StringUtils.isEmpty(value)){
                return;
            }
            //有值就转成int
            int num =Integer.parseInt(value+"");
            //把redis的num加一
            redisTemplate.opsForValue().set("num",++num);
            //释放锁
            redisTemplate.delete("lock");
        }else{
            //获取锁失败，每隔0.1秒再获取
            try {
                Thread.sleep(100);
                testLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    @RequestMapping("/doseckill")
    public boolean miaosha( @RequestParam(defaultValue = "134",required = false) String uid, String prodid){
        //1 uid和prodid非空判断
        if(uid == null || prodid == null) {
            return false;
        }
        System.out.println(prodid);
        // 3.1 库存key
        String kcKey = "sk:"+prodid+":qt";
        // 3.2 秒杀成功用户key
        String userKey = "sk:"+prodid+":user";
        //监视库存
        redisTemplate.watch(kcKey);
        //4 获取库存，如果库存null，秒杀还没有开始
        Integer kc = (Integer) redisTemplate.opsForValue().get(kcKey);
        System.out.println("获取到的库存为："+kc);
        if(kc == null) {
            System.out.println("秒杀还没有开始，请等待");
            return false;
        }
        // 5 判断用户是否重复秒杀操作
        if(redisTemplate.boundSetOps(userKey).isMember(uid)) {
            System.out.println("已经秒杀成功了，不能重复秒杀");
            return false;
        }
        //6 判断如果商品数量，库存数量小于1，秒杀结束
        if(kc<=0) {
            System.out.println("秒杀已经结束了");
            return false;
        }

        //7 秒杀过程

        /**
         * 通过 SessionCallback，保证所有的操作都在同一个 Session 中完成
         * 更常见的写法仍是采用 RedisTemplate 的默认配置，即不开启事务支持。但是，
         * 我们可以通过使用 SessionCallback，该接口保证其内部所有操作都是在同一个Session中。测试代码如下：
         */

        SessionCallback<Object> callback = new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                operations.opsForValue().decrement(kcKey);
                operations.opsForSet().add(userKey,uid);
                return operations.exec();
            }
        };
        List results = (List) redisTemplate.execute(callback);
        // [true, true, true]
        System.out.println(redisTemplate.execute(callback));

        if(results == null || results.size()==0) {
            System.out.println("秒杀失败了....");
            return false;
        }

        System.out.println("秒杀成功了..");

        return true;
    }


}
